<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        // 普通的add函数
        function add(x, y) {
            return x + y
        }

        // Currying后
        function curryingAdd(x) {
            return function (y) {
                return x + y
            }
        }

        add(1, 2)           // 3
        curryingAdd(1)(2)   // 3
        //实际上就是把add函数的x，y两个参数变成了先用一个函数接收x然后返回一个函数去处理y参数。
        //现在思路应该就比较清晰了，就是只传递给函数一部分参数来调用它，
        //让它返回一个函数去处理剩下的参数。



        //来列一列Currying有哪些好处？
        //1.参数复用

        // 正常正则验证字符串 reg.test(txt)
        // 函数封装后
        function check(reg, txt) {
            return reg.test(txt)
        }

        check(/\d+/g, 'test')       //false
        check(/[a-z]+/g, 'test')    //true

        // Currying后
        function curryingCheck(reg) {
            return function (txt) {
                return reg.test(txt)
            }
        }

        var hasNumber = curryingCheck(/\d+/g)
        var hasLetter = curryingCheck(/[a-z]+/g)

        hasNumber('test1')      // true
        hasNumber('testtest')   // false
        hasLetter('21212')      // false
        //上面的示例是一个正则的校验，正常来说直接调用check函数就可以了，
        //但是如果我有很多地方都要校验是否有数字，其实就是需要将第一个参数reg进行复用，
        //这样别的地方就能够直接调用hasNumber，hasLetter等函数，让参数能够复用，调用起来也更方便。

        //2.提前确认

        // var on = function (element, event, handler) {
        //     if (document.addEventListener) {
        //         if (element && event && handler) {
        //             element.addEventListener(event, handler, false);
        //         }
        //     } else {
        //         if (element && event && handler) {
        //             element.attachEvent('on' + event, handler);
        //         }
        //     }
        // }
        // var on = (function () {
        //     if (document.addEventListener) {
        //         return function (element, event, handler) {
        //             if (element && event && handler) {
        //                 element.addEventListener(event, handler, false);
        //             }
        //         };
        //     } else {
        //         return function (element, event, handler) {
        //             if (element && event && handler) {
        //                 element.attachEvent('on' + event, handler);
        //             }
        //         };
        //     }
        // })();
        // //换一种写法可能比较好理解一点，上面就是把isSupport这个参数给先确定下来了
        // var on = function (isSupport, element, event, handler) {
        //     isSupport = isSupport || document.addEventListener;
        //     if (isSupport) {
        //         return element.addEventListener(event, handler, false);
        //     } else {
        //         return element.attachEvent('on' + event, handler);
        //     }
        // }
        //我们在做项目的过程中，封装一些dom操作可以说再常见不过，上面第一种写法也是比较常见，
        //但是我们看看第二种写法，它相对一第一种写法就是自执行然后返回一个新的函数，
        //这样其实就是提前确定了会走哪一个方法，避免每次都进行判断。



        //3. 延迟运行

        Function.prototype.bind = function (context) {
            var _this = this
            var args = Array.prototype.slice.call(arguments, 1)
        
            return function() {
                return _this.apply(context, args)
            }
        }
        //像我们js中经常使用的bind，实现的机制就是Currying.
        //说了这几点好处之后，发现还有个问题，难道每次使用Currying都要对底层函数去做修改，



        //有没有什么通用的封装方法？
        // 初步封装
        var currying = function(fn) {
            // args 获取第一个方法内的全部参数
            var args = Array.prototype.slice.call(arguments, 1)
            return function() {
                // 将后面方法里的全部参数和args进行合并
                var newArgs = args.concat(Array.prototype.slice.call(arguments))
                // 把合并后的参数通过apply作为fn的参数并执行
                return fn.apply(this, newArgs)
            }
        }
        //这边首先是初步封装,通过闭包把初步参数给保存下来，然后通过获取剩下的arguments进行拼接，
        //最后执行需要currying的函数。
        //但是好像还有些什么缺陷，这样返回的话其实只能多扩展一个参数，
        //currying(a)(b)(c)这样的话，貌似就不支持了（不支持多参数调用），
        //一般这种情况都会想到使用递归再进行封装一层。
        // 支持多参数传递
        function progressCurrying(fn, args) {
            var _this = this
            var len = fn.length;
            var args = args || [];
            return function() {
                var _args = Array.prototype.slice.call(arguments);
                Array.prototype.push.apply(args, _args);

                // 如果参数个数小于最初的fn.length，则递归调用，继续收集参数
                if (_args.length < len) {
                    return progressCurrying.call(_this, fn, _args);
                }
                
                // 参数收集完毕，则执行fn
                return fn.apply(this, _args);
            }
        }

    </script>
</body>

</html>